package com.yungu.swift.lbs.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.collect.Lists;
import com.yungu.swift.autoconfig.redis.RedisCacheService;
import com.yungu.swift.base.ResponseData;
import com.yungu.swift.constants.OrderConstant;
import com.yungu.swift.lbs.dao.MongoDaoHelper;
import com.yungu.swift.lbs.model.*;
import com.yungu.swift.lbs.model.dto.DriverWorkTimeDto;
import com.yungu.swift.lbs.model.dto.LatLngDto;
import com.yungu.swift.lbs.model.vo.GeoVo;
import com.yungu.swift.lbs.model.vo.RegeoVo;
import com.yungu.swift.lbs.service.LocationService;
import com.yungu.swift.lbs.trace.gaode.TerminalUtils;
import com.yungu.swift.lbs.trace.gaode.TraceUtils;
import com.yungu.swift.lbs.trace.google.GooglePolylineUtils;
import com.yungu.swift.lbs.utils.GeoHash;
import com.yungu.swift.lbs.utils.LocationUtils;
import com.yungu.swift.utils.DateUtils;
import com.yungu.swift.utils.JsonUtils;
import com.yungu.swift.utils.MapUtils;
import com.yungu.swift.utils.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.yungu.swift.lbs.config.NacosListener.THIRDPARTY_CONFIG;


/**
 * 位置信息服务Service
 * Created by cuixiuyin on 2018/12/7.
 */
@Service
public class LocationServiceImpl implements LocationService {

    private static final Logger logger = LoggerFactory.getLogger(LocationServiceImpl.class);
    @Autowired
    private MongoOperations mongoOperations;
    @Autowired
    private RedisCacheService redisCacheService;

    private double getDoubleValue(Object object) {
        return object == null ? 0d : (Double) object;
    }

    @Override
    public ResponseData<Boolean> uploadDriverLocation(Map<String, Object> paramsMap) {
        String uuid = (String) paramsMap.get("driverUuid");
        Integer type = (Integer) paramsMap.get("driverType");
        double lng = getDoubleValue(paramsMap.get("lng"));
        double lat = getDoubleValue(paramsMap.get("lat"));
        double distance = getDoubleValue(paramsMap.get("distance"));
        String orderUuid = (String) paramsMap.get("orderUuid");
        double angle = getDoubleValue(paramsMap.get("angle"));
        String appid = (String) paramsMap.get("appid");
        Integer orderStatus = (Integer) paramsMap.get("orderStatus");
        double mileage = getDoubleValue(paramsMap.get("mileage"));
        double fee = getDoubleValue(paramsMap.get("totalFare"));
        String areaCode = (String) paramsMap.get("areaCode");
        double elevation = getDoubleValue(paramsMap.get("elevation"));
        double speed = getDoubleValue(paramsMap.get("speed"));
        String bizStatus = (String) paramsMap.get("bizStatus");
        double accuracy = getDoubleValue(paramsMap.get("accuracy"));
        String positionType = (String) paramsMap.get("positionType");
        String carModelType = (String) paramsMap.get("carModelType");
        String appVersion = (String) paramsMap.get("appVersion");
        String phoneVersion = (String) paramsMap.get("phoneVersion");
        String mainOrderUuid = (String) paramsMap.get("mainOrderUuid");
        Integer mainJoinStatus = paramsMap.get("mainJoinStatus") == null ? null : Integer.parseInt(paramsMap.get("mainJoinStatus").toString());
        Integer joinStatus = paramsMap.get("joinStatus") == null ? null : Integer.parseInt(paramsMap.get("joinStatus").toString());
        return uploadDriverLocationMethod(uuid, type, lng, lat, distance, new Date(), orderUuid, angle, appid,
                orderStatus, mileage, fee, areaCode, elevation, speed, bizStatus, accuracy, positionType, carModelType,
                appVersion, phoneVersion, mainOrderUuid, mainJoinStatus, joinStatus);
    }


    @Override
    public ResponseData<Boolean> uploadDriverLocation(String uuid, int type, double lng, double lat, double distance,
                                                      Date uploadTime, String orderUuid, double angle, String appid,
                                                      int orderStatus, double mileage, double fee) {
        return uploadDriverLocationMethod(uuid, type, lng, lat, distance, uploadTime, orderUuid, angle, appid,
                orderStatus, mileage, fee, null, null, null, null, null,
                null, null, null, null, null, null, null);
    }


    @Override
    public ResponseData<List<DriverPoint>> geoNear(int type, double lng, double lat, String appid, int distance, int maxNum) {
        return geoNearOnlineMethod(type, lng, lat, appid, distance, maxNum, false);
    }


    @Override
    public ResponseData<List<DriverPoint>> geoNearOnline(int type, double lng, double lat, String appid, int distance, int maxNum) {
        return geoNearOnlineMethod(type, lng, lat, appid, distance, maxNum, true);
    }


    @Override
    public ResponseData<List<DriverLocation>> getAllNewsDriverPoint(int type, int carType, String appid) {
        List<DriverLocation> points = MongoDaoHelper.getAllNewsDriverPoint(type, carType, appid);
        if (CollectionUtils.isNotEmpty(points)) {
            // 检索结果转存
            List<DriverLocation> lstDriverPoint = new ArrayList<>();
            //这里只存每个司机的第一个点，也就是最新的点
            String driverUuid = "";
            for (DriverLocation point : points) {
                if (!point.getDriverUuid().equals(driverUuid)) {
                    driverUuid = point.getDriverUuid();
                    lstDriverPoint.add(point);
                }
            }
            return ResponseData.buildSuccessResponse(lstDriverPoint);
        }
        return ResponseData.buildSuccessResponse(Lists.newArrayList());
    }

    @Override
    public ResponseData<DriverPoint> findDriverPoint(String driverUuid, String appid) {
        DriverPoint srhParam = new DriverPoint();
        srhParam.setDriverUuid(driverUuid);
        List<DriverPoint> driverPointList = MongoDaoHelper.findDriverPoint(srhParam);
        if (driverPointList == null || driverPointList.size() == 0) {
            return ResponseData.buildSuccessResponse(null);
        }
        return ResponseData.buildSuccessResponse(driverPointList.get(0));
    }

    @Override
    public ResponseData<DriverLocation> findDriverLocation(String driverUuid, String appid) {
        DriverLocation srhParam = new DriverLocation();
        srhParam.setDriverUuid(driverUuid);
        List<DriverLocation> driverLocationList = MongoDaoHelper.findDriverLocation(srhParam);
        if (driverLocationList == null || driverLocationList.size() == 0) {
            return ResponseData.buildSuccessResponse(null);
        }
        return ResponseData.buildSuccessResponse(driverLocationList.get(0));
    }


    @Override
    public ResponseData<DriverLocation> findNewestOrderLocation(String orderUuid, String appid) {
        DriverLocation driverPoint = MongoDaoHelper.findNewestOrderLocation(orderUuid, appid);
        if (driverPoint == null || StringUtils.isEmpty(driverPoint.get_id())) {
            return ResponseData.buildSuccessResponse(null);
        }
        return ResponseData.buildSuccessResponse(driverPoint);
    }

    @Override
    public ResponseData<DriverLocation> findOrderLocationByPositionType(String orderUuid, String positionType, String appid) {
        DriverLocation driverLocation = MongoDaoHelper.findNewestOrderLocation(orderUuid, positionType, appid);
        if (driverLocation == null || StringUtils.isEmpty(driverLocation.get_id())) {
            return ResponseData.buildSuccessResponse(null);
        }
        return ResponseData.buildSuccessResponse(driverLocation);
    }

    @Override
    public ResponseData<List<DriverLocation>> findOrderMapPoint(String orderUuid, String appid) {
        return ResponseData.buildSuccessResponse(MongoDaoHelper.findOrderMapPoint(orderUuid, appid));
    }

    @Override
    public ResponseData<List<DriverLocation>> findDriverTodayMapPoint(String driverUuid, String appid) {
        return ResponseData.buildSuccessResponse(MongoDaoHelper.findDriverTodayMapPoint(driverUuid, appid));
    }

    @Override
    public ResponseData<List<DriverWorkTimeDto>> getWorkTimeList() {
        List<DriverWorkTimeDto> workTimeDtos = new ArrayList<>();
        try {
            Iterator driverList = mongoOperations.getCollection("driverLocation").distinct("driverUuid").iterator();
            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.DAY_OF_MONTH, -1);
            String dateStr = DateUtils.format(cal.getTime(), "yyyy-MM-dd");
            Date dateStart = DateUtils.parse(dateStr + " 00:00:00", "yyyy-MM-dd HH:mm:ss");
            Date dateEnd = DateUtils.parse(dateStr + " 23:59:59", "yyyy-MM-dd HH:mm:ss");
            while (driverList.hasNext()) {
                String driverUuid = (String) driverList.next();
                Query query = new Query();
                query.addCriteria(new Criteria("driverUuid").is(driverUuid));
                query.addCriteria(new Criteria("positionType").is("AUTO"));
                query.addCriteria(new Criteria("uploadTime").gte(dateStart).lte(dateEnd));
                List<DriverLocation> driverPoints = mongoOperations.find(query, DriverLocation.class);
                if (!driverPoints.isEmpty()) {
                    logger.info("driverUuid:" + driverUuid + "|dateStr:" + dateStr + "|workTime:" + driverPoints.size() * 15);
                    DriverWorkTimeDto workTimeDto = new DriverWorkTimeDto();
                    workTimeDto.setUuid(StringUtils.buildUUID());
                    workTimeDto.setDriverUuid(driverUuid);
                    workTimeDto.setWorkDate(dateStr);
                    workTimeDto.setWorkTime(driverPoints.size() * 15);
                    workTimeDto.setCreateOn(new Date());
                    workTimeDtos.add(workTimeDto);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseData.buildSuccessResponse(workTimeDtos);
    }


    @Override
    public ResponseData<List<OrderEventPoint>> listOrderEventPoint(String orderUuid) {
        Query query = new Query();
        query.addCriteria(new Criteria("orderUuid").is(orderUuid));
        query.addCriteria(new Criteria("positionType").ne("AUTO"));
        query.with(new Sort(new Sort.Order(Sort.Direction.ASC, "uploadTime")));
        List<DriverLocation> driverLocationList = mongoOperations.find(query, DriverLocation.class);
        if (CollectionUtils.isNotEmpty(driverLocationList)) {
            // 1.过滤掉没有经纬度的点
            List<DriverLocation> driverLocations = driverLocationList.stream().parallel().filter(driverLocation ->
                    driverLocation.getCoordinate() != null).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(driverLocations)) {
                // 2：数据按20为准分堆（高德接口最多只能一次查询20条）
                List<List<DriverLocation>> listByQuantity = groupListByQuantity(driverLocations, 20);
                // 3：处理访问数据
                if (CollectionUtils.isNotEmpty(listByQuantity)) {
                    Map<String, List<OrderEventPoint>> orderEventPointListMap = new HashMap(16);
                    for (List<DriverLocation> list : listByQuantity) {
                        StringBuilder locations = new StringBuilder();
                        List<OrderEventPoint> doGaodelist = new ArrayList<>();
                        for (DriverLocation driverLocation : list) {
                            OrderEventPoint orderEventPoint = new OrderEventPoint();
                            orderEventPoint.setObjectId(driverLocation.get_id());
                            orderEventPoint.setDriverUuid(driverLocation.getDriverUuid());
                            orderEventPoint.setLng(driverLocation.getCoordinate().getLng());
                            orderEventPoint.setLat(driverLocation.getCoordinate().getLat());
                            orderEventPoint.setUploadTime(driverLocation.getUploadTime());
                            orderEventPoint.setPositionType(driverLocation.getPositionType());
                            doGaodelist.add(orderEventPoint);
                            locations.append(orderEventPoint.getLng() + "," + orderEventPoint.getLat() + "|");
                        }
                        orderEventPointListMap.put(StringUtils.removeEnd(locations.toString(), "|"), doGaodelist);
                    }
                    return ResponseData.buildSuccessResponse(batchBuildAddress(orderEventPointListMap));
                }
            }
        }
        return ResponseData.buildSuccessResponse(Lists.newArrayList());
    }

    /**
     * 集合按数量分组
     *
     * @param list
     * @param quantity
     * @param <T>
     * @return
     */
    private <T> List<List<T>> groupListByQuantity(List<T> list, int quantity) {
        if (list == null || list.size() == 0 || quantity < 1) {
            return null;
        }
        List<List<T>> result = new ArrayList<>();
        int size = list.size();
        int count = (size + quantity - 1) / quantity;

        for (int i = 0; i < count; i++) {
            List<T> subList = list.subList(i * quantity, ((i + 1) * quantity > size ? size : quantity * (i + 1)));
            result.add(subList);
        }
        return result;
    }

    @Override
    public ResponseData<Map<String, Object>> getTodayLocationsByStatsStatus(String driverUuid, int statsStatus, String currentOrderUuid, Date startTime) {
        // 如果司机uuid或者位置类型不存在 则直接返回null
        if (0 == statsStatus || StringUtils.isEmpty(driverUuid) || null == startTime) {
            return null;
        }
        Map<String, Object> map = MapUtils.build(3);
        Query locationQuery = new Query();
        locationQuery.addCriteria(new Criteria("driverUuid").is(driverUuid));
        if (StringUtils.isNotEmpty(currentOrderUuid)) {
            locationQuery.addCriteria(new Criteria("orderUuid").is(currentOrderUuid));
        }
        // 大于上一个状态的上传时间 不能加上等于
        locationQuery.addCriteria(new Criteria("uploadTime").gt(startTime));
        map.put("locationList", mongoOperations.find(locationQuery, DriverLocation.class));
        return ResponseData.buildSuccessResponse(map);
    }

    @Override
    public ResponseData<Boolean> copyLastPointAsOffWorkPoint(String driverUuid) {
        ResponseData<DriverLocation> responseData = findDriverLocation(driverUuid, null);
        if (responseData.isSuccess() && responseData.getData() != null) {
            DriverLocation driverLocation = responseData.getData();
            driverLocation.set_id(null);
            driverLocation.setPositionType("SJXB");
            driverLocation.setCreateTime(new Date());
            driverLocation.setUploadTime(new Date());
            MongoDaoHelper.insert(driverLocation);
            return ResponseData.buildSuccessResponse("插入成功", Boolean.TRUE);
        }
        return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "插入失败");
    }

    @Override
    public ResponseData<List<DriverPoint>> getScopeDriver(Map<String, Object> map) {
        List<DriverPoint> geoResults = this.mongoOperations.find(buildSphericalModel(map), DriverPoint.class);
        //mongoDB查询不到司机的情况
        if (CollectionUtils.isEmpty(geoResults)) {
            logger.info("经度[{}]-纬度[{}]-范围[{}]公里，内无司机", map.get("longitude"), map.get("latitude"), map.get("maxDistance"));
            return ResponseData.buildSuccessResponse(Lists.newArrayList());
        }
        //去重排序
        return ResponseData.buildSuccessResponse(uniqueSortDriver(geoResults,(Double)map.get("longitude"),(Double)map.get("latitude")));
    }

    @Override
    public ResponseData<DriverLocation> getOrderPointByPositionType(String orderUuid, String positionType) {
        Query query = new Query();
        query.addCriteria(new Criteria("orderUuid").is(orderUuid));
        query.addCriteria(new Criteria("positionType").is(positionType));
        query.with(new Sort(new Sort.Order(Sort.Direction.ASC, "uploadTime")));
        query.limit(1);
        List<DriverLocation> driverLocations = mongoOperations.find(query, DriverLocation.class);
        if (driverLocations != null && driverLocations.size() > 0) {
            return ResponseData.buildSuccessResponse(driverLocations.get(0));
        }
        return ResponseData.buildSuccessResponse(null);
    }

    @Override
    public ResponseData<GeoVo> geo(String address) {
        return ResponseData.buildSuccessResponse("获取地理位置编码信息成功", LocationUtils.geo(address));
    }

    @Override
    public ResponseData<RegeoVo> regeo(Double lng, Double lat) {
        return ResponseData.buildSuccessResponse("获取（逆）地理位置编码成功", LocationUtils.regeo(lng, lat));
    }

    @Override
    public ResponseData<String> getAddress(Double lng, Double lat) {
        String address = "";
        ResponseData<RegeoVo> pointJson = regeo(lng, lat);
        if (pointJson.isSuccess() && pointJson.getData() != null) {
            RegeoVo regeoVo = pointJson.getData();
            address = regeoVo.getFormattedAddress();
        }
        return ResponseData.buildSuccessResponse(address);
    }

    @Override
    public ResponseData<Double> getDrivingDistance(String vehicleId, long startTime, long endTime) {
        Map<String, Object> drivingDistance = LocationUtils.getDrivingDistance(vehicleId, startTime, endTime);
        Double distance = 0D;
        if (drivingDistance != null && drivingDistance.containsKey("distance")) {
            distance = (Double) drivingDistance.get("distance");
        }
        return ResponseData.buildSuccessResponse(distance);
    }

    @Override
    public ResponseData<List<DriverLocation>> findOrderDuringTheTripPoints(String orderUuid, String appid) {
        List<DriverLocation> driverPoint = MongoDaoHelper.findOrderDuringTheTripPoints(orderUuid, appid);
        if (driverPoint.isEmpty()) {
            return ResponseData.buildSuccessResponse(Lists.newArrayList());
        }
        return ResponseData.buildSuccessResponse(driverPoint);
    }

    private ResponseData<Boolean> uploadDriverLocationMethod(String uuid, int type, double lng, double lat, double distance,
                                                             Date uploadTime, String orderUuid, double angle, String appid,
                                                             int orderStatus, double mileage, double fee, String areaCode,
                                                             Double elevation, Double speed, String bizStatus, Double accuracy,
                                                             String positionType, String carModelType, String appVersion,
                                                             String phoneVersion, String mainOrderUuid, Integer mainJoinStatus, Integer joinStatus) {
        // 记录司机行驶轨迹信息
        DriverLocation location = new DriverLocation();
        // 司机UUID
        location.setDriverUuid(uuid);
        // 上传位置信息时间
        location.setUploadTime(uploadTime);
        // 与上一点距离
        location.setDistance(distance);
        // 订单UUID
        if (StringUtils.isEmpty(orderUuid)) {
            location.setOrderUuid(null);
            location.setStatus(DriverPoint.STATUS_ONLINE);
        } else {
            location.setOrderUuid(orderUuid);
            location.setStatus(DriverPoint.STATUS_BUSY);
            //这边在订单状态为300、400时上传定位点到猎鹰
            if (orderStatus == OrderConstant.ORDER_STATUS_START || orderStatus == OrderConstant.ORDER_STATUS_ARRIVED) {
                String key = THIRDPARTY_CONFIG.getGaodeKey();
                String sid = THIRDPARTY_CONFIG.getGaodeSid();
                // 终端id
                Long tid;
                String tName = "driver_" + uuid;
                Object obj = redisCacheService.get(tName);
                if (obj != null) {
                    tid = Long.parseLong(obj.toString());
                } else {
                    List<TerminalUtils.TerminalInfo> list = TerminalUtils.listTerminal(key, sid, null, tName, null);
                    if (CollectionUtils.isNotEmpty(list)) {
                        TerminalUtils.TerminalInfo terminalInfo = list.get(0);
                        tid = terminalInfo.getTid();
                        redisCacheService.set(tName, tid.toString(), 10L);
                    } else {
                        // 创建终端
                        tid = TerminalUtils.createTerminal(key, sid, tName);
                        redisCacheService.set(tName, tid.toString(), 10L);
                    }
                }
                // 轨迹id
                Long trid;
                Criteria criteria = Criteria.where("orderUuid").is(orderUuid);
                criteria.and("orderStatus").is(OrderConstant.ORDER_STATUS_START);
                Query query = new Query(criteria);
                DriverLocation driverLocation = mongoOperations.findOne(query, DriverLocation.class);
                if (driverLocation != null && driverLocation.getTrid() != null) {
                    trid = Long.parseLong(driverLocation.getTrid());
                } else {
                    trid = TraceUtils.createTrace(key, sid, tid);
                }
                location.setTrid(trid.toString());
                // 上传轨迹
                Point point = new Point();
                point.setLocation(new BigDecimal(lng).setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue()
                        + "," + new BigDecimal(lat).setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue());
                point.setLocatetime(uploadTime.getTime());
                point.setSpeed(speed);
                point.setAccuracy(accuracy);
                point.setHeight(elevation);
                point.setDirection(angle);
                TraceUtils.uploadPoints(key, sid, tid, trid, Lists.newArrayList(point));
            }
        }
        // 订单中累计距离
        location.setMileage(mileage);
        // 订单中实时费用
        location.setFee(fee);
        location.setCreateTime(new Date());
        // 计算GeoHash并返还
        GeoHash geoHash = new GeoHash(lng, lat);
        location.setGeohash(geoHash.getGeoHashBase32());
        // 位置信息
        Coordinate coordinate = new Coordinate();
        coordinate.setLng(lng);
        coordinate.setLat(lat);
        location.setCoordinate(coordinate);
        // 当前角度
        location.setAngle(angle);
        // 应用ID
        location.setAppid(appid);
        // 司机类型
        location.setType(type);
        // 订单状态
        location.setOrderStatus(orderStatus);
        //areaCode：区域编码   elevation：海拔高度     speed：瞬时速度   bizStatus：营运状态 positonType:状态信息  carModelType:车型
        location.setAreaCode(areaCode);
        location.setElevation(elevation);
        location.setSpeed(speed);
        location.setBizStatus(bizStatus);
        location.setAccuracy(accuracy);
        location.setPositionType(positionType);
        location.setCarModelType(carModelType);
        location.setAppVersion(appVersion);
        location.setPhoneVersion(phoneVersion);
        location.setMainOrderUuid(mainOrderUuid);
        location.setMainJoinStatus(mainJoinStatus);
        location.setJoinStatus(joinStatus);

        // 存储行驶轨迹信息
        MongoDaoHelper.insert(location);
        // 更新最新位置信息
        DriverPoint point = new DriverPoint();
        point.setDriverUuid(uuid);
        point.setUploadTime(uploadTime);
        point.setCoordinate(coordinate);
        // 应用ID
        point.setAppid(appid);
        point.setAngle(angle);
        // 司机类型
        point.setType(type);
        point.setStatus(StringUtils.isNotEmpty(orderUuid) ? DriverPoint.STATUS_BUSY : DriverPoint.STATUS_ONLINE);
        MongoDaoHelper.insertDriverPoint(point);
        return ResponseData.buildSuccessResponse("成功上传司机位置", Boolean.TRUE);
    }


    private ResponseData<List<DriverPoint>> geoNearOnlineMethod(int type, double lng, double lat, String appid, int distance, int maxNum, boolean online) {
        // 检索附近的司机
        List<DriverPoint> geoResults;
        if (online) {
            geoResults = MongoDaoHelper.geoNearOnline(type, lng, lat, appid, distance, maxNum);
        } else {
            geoResults = MongoDaoHelper.geoNear(type, lng, lat, appid, distance, maxNum);
        }
        // 检索结果转存
        List<DriverPoint> lstDriverPoint = new ArrayList<>();
        //这里只存每个司机的第一个点，也就是最新的点
        String driverUuid = "";
        for (DriverPoint driverPoint : geoResults) {
            Coordinate coordinate  = driverPoint.getCoordinate();
            driverPoint.setDistance(LocationUtils.getDistance(coordinate.getLng(),coordinate.getLat(),lng,lat));
            if (!driverPoint.getDriverUuid().equals(driverUuid)) {
                driverUuid = driverPoint.getDriverUuid();
                lstDriverPoint.add(driverPoint);
            }
        }
        //按照distance字段排序
        Collections.sort(lstDriverPoint, new Comparator() {
            @Override
            public int compare(Object obj1, Object obj2) {
                DriverPoint point1 = (DriverPoint) obj1;
                DriverPoint point2 = (DriverPoint) obj2;
                return point1.getDistance().compareTo(point2.getDistance());
            }
        });
        return ResponseData.buildSuccessResponse(lstDriverPoint);
    }

    private Query buildSphericalModel(Map<String, Object> map) {
        org.springframework.data.geo.Point point = new org.springframework.data.geo.Point((Double) map.get("longitude"), (Double) map.get("latitude"));
        Criteria criteria = Criteria.where("coordinate").nearSphere(point);
        criteria.maxDistance((Integer) map.get("maxDistance") * 1000 / MongoDaoHelper.EARTH_RADIUS);
        //绑定查询语句(5分钟内，有上传位置的司机)
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.MINUTE, -5);
        criteria.and("uploadTime").gte(calendar.getTime());
        // 最多1000个点防止 mongo 拖垮系统
        return new Query(criteria).limit(1000);
    }

    /**
     * 对距离范围的司机进行去重和排序
     *
     * @param geoResults 范围内司机集合
     * @return List<String>
     */
    private List<DriverPoint> uniqueSortDriver(List<DriverPoint> geoResults,Double lng,Double lat) {
        // 赋值每个点距离
        return geoResults.stream().parallel()
                .map(geoResult -> {
                    Coordinate coordinate = geoResult.getCoordinate();
                    geoResult.setDistance(LocationUtils.getDistance(coordinate.getLng(),coordinate.getLat(),lng,lat));
                    return geoResult;
                }).collect(Collectors.toList());
    }

    /**
     * 批量查询
     *
     * @param orderEventPointListMap
     * @return
     */
    private static List<OrderEventPoint> batchBuildAddress(Map<String, List<OrderEventPoint>> orderEventPointListMap) {
        List<OrderEventPoint> result = new ArrayList<>();
        Set<Map.Entry<String, List<OrderEventPoint>>> entrySet = orderEventPointListMap.entrySet();
        Iterator<Map.Entry<String, List<OrderEventPoint>>> iterator = entrySet.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, List<OrderEventPoint>> entry = iterator.next();
            Map<String, Object> map = LocationUtils.getBatchPointsJson(entry.getKey());
            String status = String.valueOf(map.get("status"));
            String info = String.valueOf(map.get("info"));
            if ("1".equals(status) && "OK".equals(info)) {
                List<OrderEventPoint> value = entry.getValue();
                List regeocodes = (List) map.get("regeocodes");
                if (regeocodes.size() == value.size()) {
                    for (int i = 0; i < regeocodes.size(); i++) {
                        Map regeocode = (Map) regeocodes.get(i);
                        getOrderEventPoint(value.get(i), regeocode);
                        result.add(value.get(i));
                    }
                }
            }
        }
        return result;
    }

    /**
     * 从regeocode 获取 orderEventAddress
     *
     * @param orderEventPoint
     * @param regeocode
     */
    private static void getOrderEventPoint(OrderEventPoint orderEventPoint, Map<String, Object> regeocode) {
        OrderEventAddress orderEventAddress = new OrderEventAddress();
        orderEventAddress.setDetailAddress(String.valueOf(regeocode.get("formatted_address")));
        //参数组件
        Map addressComponent = (Map) regeocode.get("addressComponent");
        String province = String.valueOf(addressComponent.get("province"));
        if ("[]".equals(province)) {
            province = "";
        }
        String city = String.valueOf(addressComponent.get("city"));
        if ("[]".equals(city)) {
            city = "";
        }
        String district = String.valueOf(addressComponent.get("district"));
        if ("[]".equals(district)) {
            district = "";
        }
        String township = String.valueOf(addressComponent.get("township"));
        if ("[]".equals(township)) {
            township = "";
        }
        orderEventAddress.setAddress(orderEventAddress.getDetailAddress().replaceAll(province, "").
                replaceAll(city, "").replaceAll(district, "").replaceAll(township, ""));
        orderEventPoint.setOrderEventAddress(orderEventAddress);
    }

    @Override
    public ResponseData<Map<String, Object>> getQueueDriverLocation(Map<String, Object> paraMap) {
        Coordinate centerCoordinate = (Coordinate) paraMap.get("coordinate");
        // 设置中心点
        NearQuery nearQuery = NearQuery.near(centerCoordinate.getLng(), centerCoordinate.getLat());
        // 设置距离返回米
        nearQuery.distanceMultiplier(MongoDaoHelper.EARTH_RADIUS);
        // 设置球面距离
        nearQuery.spherical(true);
        if (paraMap.get("assignRadius") != null) {
            // 设置检索半径（米）
            nearQuery.maxDistance((Double) paraMap.get("assignRadius") * 1000);
        }

        Query query = new Query();
        //寻找最近5分钟内，有上传位置的司机
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.MINUTE, -5);
        query.addCriteria(new Criteria("uploadTime").gte(calendar.getTime()));
        List<String> driverUuidList = (ArrayList) paraMap.get("driverUuidList");
        query.addCriteria(Criteria.where("driverUuid").in(driverUuidList));
        nearQuery.query(query);

        GeoResults<DriverPoint> geoResults = mongoOperations.geoNear(nearQuery, DriverPoint.class);
        if (geoResults != null) {
            Map<String, Object> driverPointMap = new HashMap<>();
            for (GeoResult<DriverPoint> geoResult : geoResults.getContent()) {
                DriverPoint driverPoint = geoResult.getContent();
                if (!driverPointMap.containsKey(driverPoint.getDriverUuid())) {
//                    driverPointMap.put(driverPoint.getDriverUuid(), new BigDecimal(geoResult.getDistance().getValue() / 1000).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue());
                    driverPointMap.put(driverPoint.getDriverUuid(), new BigDecimal(geoResult.getDistance().getValue() / 1000).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
                }
            }
            if (driverPointMap.size() > 0) {
                return ResponseData.buildSuccessResponse(driverPointMap);
            }
        }
        return ResponseData.buildErrorResponse(0, "未找到符合条件的司机");
    }

    @Override
    public ResponseData<List<DriverLocation>> getCrosstownOrderLocation(Map<String, Object> paraMap) {
        Query query = new Query();
        query.with(new Sort(new Sort.Order(Sort.Direction.ASC, "uploadTime")));
        query.addCriteria(new Criteria("mainOrderUuid").is(paraMap.get("mainOrderUuid")));
        query.addCriteria(new Criteria("appid").is(paraMap.get("appid")));
        List<Integer> joinStatusList = new ArrayList<>();
        joinStatusList.add(OrderConstant.ORDER_JOIN_STATUS_DRIVER_DEPARTURE);
        joinStatusList.add(OrderConstant.ORDER_JOIN_STATUS_STARTED);
        joinStatusList.add(OrderConstant.ORDER_JOIN_STATUS_COMPLETED);

        query.addCriteria(Criteria.where("mainJoinStatus").in(joinStatusList));
        List<DriverLocation> lstDriverLocation = mongoOperations.find(query, DriverLocation.class);

        if (lstDriverLocation != null && lstDriverLocation.size() > 0) {
            return ResponseData.buildSuccessResponse("查询成功", lstDriverLocation);
        }
        return ResponseData.buildSuccessResponse("未找到定位点", new ArrayList<>());
    }

    /**
     * 获取预估路线
     * @return
     */
    @Override
    public ResponseData<String> getEstimateRoute(Double originLng, Double originLat, Double destLng, Double destLat) {
        try{
            List<LatLngDto> locationList = new ArrayList<>();
            //获取高德规划路径详情 steps导航路段集合  polyline路段坐标点串  格式 steps:["0":{"polyline":"116.480888,39.989368;116.480576,39.989571"}]
            Map<String, Object> resultMap = LocationUtils.getDriving(originLng.toString(),originLat.toString(),destLng.toString(),destLat.toString());
            List<Map<String,Object>> steps = (List<Map<String,Object>>)resultMap.get("steps");
            for(int i=0;i<steps.size();i++){
                Map<String,Object> stepMap = steps.get(i);
                String[] polylineArr = stepMap.get("polyline").toString().split(";");
                //每个路段只取最多2个点 取第一个 中间 下个路段第一个点与当前最后一个点相同
                locationList.add(buildLatLng(polylineArr[0]));
                if(polylineArr.length>2){
                    locationList.add(buildLatLng(polylineArr[polylineArr.length/2]));
                }
                //当最后一段路 取出最后一个点
                if(i==steps.size()-1){
                    locationList.add(buildLatLng(polylineArr[polylineArr.length-1]));
                }
            }
            Integer listSize = locationList.size();
            //只保留16个点
            while (listSize>16){
                List<LatLngDto> removeList = new ArrayList<>();
                for(int i = 1;i<locationList.size()-1;i=i+5){
                    LatLngDto latLngDto = locationList.get(i);
                    removeList.add(latLngDto);
                }
                listSize = listSize-removeList.size();
                locationList.removeAll(removeList);
            }

            logger.info("高德得到的轨迹点{}",JsonUtils.toJSONString(locationList));
            return ResponseData.buildSuccessResponse(GooglePolylineUtils.encodePoly(locationList));
        }catch (Exception e){
            e.printStackTrace();
            logger.info("获取预估路线位置点失败");
        }

        return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE,"获取失败");
    }

    /**
     * 设置经纬度对象
     */
    private LatLngDto buildLatLng(String polylineStr){
        String[] locationStr = polylineStr.split(",");
        LatLngDto latLngDto = new LatLngDto();
        latLngDto.setLongitude(Double.parseDouble(locationStr[0]));
        latLngDto.setLatitude(Double.parseDouble(locationStr[1]));
        return latLngDto;
    }

    /**
     * 通过主单ID获取位置点
     * @param mainOrderUuid
     * @return
     */
    @Override
    public ResponseData<List<DriverLocation>> findMainOrderLocation(String mainOrderUuid) {
        Query query = new Query();
        query.addCriteria(new Criteria("mainOrderUuid").is(mainOrderUuid));
        query.with(new Sort(new Sort.Order(Sort.Direction.ASC, "uploadTime")));
        List<DriverLocation> driverLocationList = mongoOperations.find(query, DriverLocation.class);
        return ResponseData.buildSuccessResponse(driverLocationList);
    }
}
